import { DubApiError, ErrorCodes } from "@/lib/api/errors";
import { createLink, processLink } from "@/lib/api/links";
import { validatePartnerLinkUrl } from "@/lib/api/links/validate-partner-link-url";
import { getProgramEnrollmentOrThrow } from "@/lib/api/programs/get-program-enrollment-or-throw";
import { parseRequestBody } from "@/lib/api/utils";
import { extractUtmParams } from "@/lib/api/utm/extract-utm-params";
import { withPartnerProfile } from "@/lib/auth/partner";
import { PartnerProfileLinkSchema } from "@/lib/zod/schemas/partner-profile";
import { createPartnerLinkSchema } from "@/lib/zod/schemas/partners";
import { prisma } from "@dub/prisma";
import { NextResponse } from "next/server";
import { z } from "zod";

// GET /api/partner-profile/programs/[programId]/links - get a partner's links in a program
export const GET = withPartnerProfile(async ({ partner, params }) => {
  const { links, discountCodes } = await getProgramEnrollmentOrThrow({
    partnerId: partner.id,
    programId: params.programId,
    include: {
      links: true,
      discountCodes: true,
    },
  });

  // Add discount code to the links
  const linksByDiscountCode = new Map(
    discountCodes?.map((discountCode) => [discountCode.linkId, discountCode]),
  );

  const result = links.map((link) => {
    const discountCode = linksByDiscountCode.get(link.id);

    return {
      ...link,
      discountCode: discountCode?.code,
    };
  });

  return NextResponse.json(z.array(PartnerProfileLinkSchema).parse(result));
});

// POST /api/partner-profile/[programId]/links - create a link for a partner
export const POST = withPartnerProfile(
  async ({ partner, params, req, session }) => {
    const { url, key, comments } = createPartnerLinkSchema
      .pick({ url: true, key: true, comments: true })
      .parse(await parseRequestBody(req));

    const {
      program,
      links,
      tenantId,
      status,
      partnerGroup: group,
    } = await getProgramEnrollmentOrThrow({
      partnerId: partner.id,
      programId: params.programId,
      include: {
        program: true,
        links: true,
        partnerGroup: true,
      },
    });

    if (["banned", "deactivated", "rejected"].includes(status)) {
      throw new DubApiError({
        code: "forbidden",
        message: `You cannot create links in this program because you have been ${status}`,
      });
    }

    if (!program.domain || !program.url) {
      throw new DubApiError({
        code: "bad_request",
        message:
          "This program needs a domain and URL set before creating a link.",
      });
    }

    if (!group) {
      throw new DubApiError({
        code: "forbidden",
        message:
          "You’re not part of any group yet. Please reach out to the program owner to be added.",
      });
    }

    if (links.length >= group.maxPartnerLinks) {
      throw new DubApiError({
        code: "bad_request",
        message: `You have reached this program's limit of ${group.maxPartnerLinks} partner links.`,
      });
    }

    validatePartnerLinkUrl({ group, url });

    // check if the group has a UTM template
    const groupUtmTemplate = group.utmTemplateId
      ? await prisma.utmTemplate.findUnique({
          where: {
            id: group.utmTemplateId,
          },
        })
      : null;

    const { link, error, code } = await processLink({
      payload: {
        domain: program.domain,
        key: key || undefined,
        url: url || program.url,
        programId: program.id,
        tenantId,
        partnerId: partner.id,
        folderId: program.defaultFolderId,
        comments,
        trackConversion: true,
        ...(groupUtmTemplate ? extractUtmParams(groupUtmTemplate) : {}),
      },
      workspace: {
        id: program.workspaceId,
        plan: "business",
        users: [{ role: "owner" }],
      },
      userId: session.user.id, // TODO: Hm, this is the partner user, not the workspace user?
      skipFolderChecks: true, // can't be changed by the partner
      skipProgramChecks: true, // can't be changed by the partner
      skipExternalIdChecks: true, // can't be changed by the partner
    });

    if (error != null) {
      throw new DubApiError({
        code: code as ErrorCodes,
        message: error,
      });
    }

    const partnerLink = await createLink(link);

    return NextResponse.json(PartnerProfileLinkSchema.parse(partnerLink), {
      status: 201,
    });
  },
);
